<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">

    <title>V-for 列表渲染</title>
    <!--<script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.0-beta.7/vue.global.js"></script>-->
    <script type="text/javascript" src="../../js/vue.global.js"></script>

</head>
<body>
<!--vue 应用实例挂载到此ID上，这样它及其下面的标签才能使用 vue 语法获取数据-->
<div id="rootApp" style="margin: 50px">
    <h3>V-for 列表渲染</h3>
    <p>
        v-for 指令基于一个数组来渲染一个列表。<br/>
        v-for 指令的值需要使用 item in items 形式的特殊语法，其中 items 是源数据的数组，而 item 是迭代项的别名.<br/>
        vue 实例化的时候通过钩子函数created发送异步请求获取数据，然后通过v-for指令在模板中遍历显示数据。<br/>
        对于多层嵌套的 v-for，作用域的工作方式和函数的作用域很类似。每个 v-for 作用域都可以访问到父级作用域。<br/>
    </p>
    <p><b>遍历数组：</b></p>
    <!--括号中的名称可以随意定义，但是顺序必须固定，比如第一个位置无论命名什么，表示的都是循环的元素，第二位置就是索引，与命名无关-->
    <!--index 是循环的索引(从0开始)。'v-for'放在哪个标签上，就会循环哪个标签-->
    <!--可以用 of 替代 in 作为分隔符，因为它更接近 JavaScript 迭代器的语法-->
    <ul>
        <li v-for="(item,index) in titles" v-text="(index+1) +' ' + item"></li>
    </ul>

    <p><b>遍历JSON数组：</b></p>
    <ul>
        <!--遍历 json 对象数组-->
        <!--遍历每个键值对，同理括号中的名称可以随便定义，但顺序不能错，第一个是值，第二个是key，第三个索引.-->
        <li v-for="(student,index) in students">{{index +1}} 姓名：{{student.name}} , 年龄: {{student.age}}</li>
    </ul>

    <p><b>遍历JSON对象：</b></p>
    <ul>
        <!--遍历 json 对象-->
        <!--遍历每个键值对，同理括号中的名称可以随便定义，但顺序不能错，第一个是值，第二个是key，第三个索引.-->
        <li v-for="(value,key, index) of students[0]">{{index+1}} : {{key}} {{value}}</li>
    </ul>

    <h3>在 v-for 里使用值的范围</h3>
    <hr/>
    <p>v-for 可以直接接受一个整数值。在这种用例中，会将该模板基于 1...n 的取值范围重复多次。<br/></p>
    <p>注意初值是从 1 开始而非 0。<br/></p>
    <!--'v-for' 可以接受整数，此时它会把模板重复对应次数-->
    <ul>
        <li v-for="item of 10" v-bind:key="'range_' + item">{{item}}</li>
    </ul>

    <h3>在 'template' 中使用 v-for</h3>
    <hr/>
    <!--类似于 v-if，也可以利用带有 v-for 的 <template> 特殊标签来循环渲染一段包含多个元素的内容-->
    <!--最终结果中是不含 <template> 标签的-->
    <template v-for="(item,index) of 10" :key="'template_for_' + index">
        <span style="color: coral">
            <span style="font-weight: bold">{{item}}({{index}})</span>
            <span style="margin-left: 10px"></span>
        </span>
    </template>

    <h3>v-for 与 v-if 一同使用</h3>
    <hr/>
    <p>当它们位于同一个节点上时，v-if 执行优先级更高，没有权限访问 v-for 里的变量。<br/></p>
    <p>如果 v-if 需要访问 v-for 里的变量，可以把 v-for 移动到 template 标签中。<br/></p>
    <template v-for="t in titles" :key="'titlesT_' + t">
        <!--元素为 null，undefined，整数 0 时等同于 false-->
        <li v-if="t">{{ t }}</li>
    </template>

    <h3>维护状态 key 属性</h3>
    <hr/>
    <!--当 Vue 正在更新使用 v-for 渲染的元素列表时，它默认使用'就地更新'的策略。-->
    <!--如果数据项的顺序被改变，Vue 将不会移动 DOM 元素来匹配数据项的顺序，而是就地更新每个元素，并且确保它们在每个索引位置正确渲染。-->
    <!--这个默认的模式是高效的，但是只适用于不依赖子组件状态或临时 DOM 状态 (例如：表单输入值) 的列表渲染输出。-->
    <!--为了给 Vue 一个提示，以便它能跟踪每个节点的身份，从而重用和重新排序现有元素，需要为每项提供一个唯一 key 属性。-->
    <!--建议尽可能在使用 v-for 时提供 key 属性，除非遍历输出的 DOM 内容非常简单，或者是刻意依赖默认行为以获取性能上的提升。-->
    <!--不要使用对象或数组之类的非基本类型值作为 v-for 的 key，请用字符串或数值类型的值。-->
    <p>
        与会门派：<span v-for="(item,index) of titles " v-bind:key="'titles_' + (index)">{{item + '' +((index !=titles.length-1)?'、':'')}}</span>
    </p>

    <h3>数组更新检测->变更方法</h3>
    <hr/>
    <!--Vue 将被侦听的数组的变更方法进行了包裹，所以它们也将会触发视图更新,这些被包裹过的方法包括：-->
    <!--push()、pop()、shift()、unshift()、splice()、sort()、reverse()-->
    <button type="button" v-on:click="addElement()">添加元素</button>
    <button type="button" v-on:click="delElement()">删除元素</button>
    <div><span v-for="item in num" :key="'num_' + item">{{item}} &nbsp;</span></div>

</div>
</body>

<script type="text/javascript">
    const titles = ['少林', '武当', '峨眉', '华山派', '衡山派', null, undefined, 0, '0'];
    const students = [{"name": "张三", "age": "15"}, {"name": "李四", "age": "17"}, {"name": "王五", "age": "16"}];
    let num = [1, 2, 3, 4, 5];

    // 1、每个 Vue 应用都是通过用 createApp 函数创建一个新的应用实例开始, 用来在应用中注册"全局"组件
    // 2、传递给 createApp 的选项用于配置根组件
    // 3、应用实例暴露的大多数方法都会返回该同一实例，方便链式编程
    const App = Vue.createApp({
        //组件选项
        // data 定义数据, html 中可以进行获取
        data() {
            function addElement() {
                if (this.num.length > 0) {
                    this.num.push(this.num[this.num.length - 1] + 1);
                } else {
                    this.num.push(1);
                }
            }

            function delElement() {
                if (this.num.length > 0) {
                    this.num.pop();
                }
            }

            return {
                titles,
                students,
                num,
                addElement,
                delElement
            }
        }
    });

    // 4、一个应用需要被挂载(mount)到一个 DOM 元素中，当挂载(mount)应用时，该组件被用作渲染的起点
    // 5、与大多数应用方法不同的是 mount 不返回应用本身，而是返回的根组件实例(vm)
    const rootViewModel = App.mount("#rootApp");

</script>
</html>
